package common

import (
	"context"
	"encoding/json"
	"errors"
	"sync"
	"time"

	"github.com/google/uuid"
	"github.com/gorilla/websocket"
)

// MessageHandler 定义接收消息的回调函数类型
type MessageHandler func(message Message)

// ErrorHandler 定义错误处理的回调函数类型
type ErrorHandler func(err error)

// WsClient 封装 WebSocket 客户端
type WsClient struct {
	url            string             // WebSocket 服务器地址
	conn           *websocket.Conn    // WebSocket 连接
	dialer         *websocket.Dialer  // WebSocket 拨号器
	messageHandler MessageHandler     // 消息处理回调
	errorHandler   ErrorHandler       // 错误处理回调
	connMu         sync.Mutex         // 连接锁
	sendCh         chan []byte        // 发送消息通道
	closeCh        chan struct{}      // 关闭信号通道
	isConnected    bool               // 连接状态
	reconnectDelay time.Duration      // 重连延迟
	maxRetries     int                // 最大重试次数
	ctx            context.Context    // 上下文
	cancel         context.CancelFunc // 取消函数
	clientID       string             // 客户端唯一标识
}

// ClientConfig 客户端配置
type ClientConfig struct {
	URL            string
	Dialer         *websocket.Dialer
	ReconnectDelay time.Duration
	MaxRetries     int
}

// -------------------------------------------------------------------------------------------------
// 创建新的 WebSocket 客户端
// -------------------------------------------------------------------------------------------------
func NewWsClient(config ClientConfig, messageHandler MessageHandler, errorHandler ErrorHandler) (
	*WsClient, error) {
	if config.URL == "" {
		return nil, errors.New("URL cannot be empty")
	}
	if messageHandler == nil {
		return nil, errors.New("message handler cannot be nil")
	}
	if errorHandler == nil {
		return nil, errors.New("error handler cannot be nil")
	}

	if config.Dialer == nil {
		config.Dialer = websocket.DefaultDialer
	}
	if config.ReconnectDelay == 0 {
		config.ReconnectDelay = 5 * time.Second
	}
	if config.MaxRetries == 0 {
		config.MaxRetries = 5
	}

	ctx, cancel := context.WithCancel(context.Background())
	client := &WsClient{
		url:            config.URL,
		dialer:         config.Dialer,
		messageHandler: messageHandler,
		errorHandler:   errorHandler,
		sendCh:         make(chan []byte, 100),
		closeCh:        make(chan struct{}),
		reconnectDelay: config.ReconnectDelay,
		maxRetries:     config.MaxRetries,
		ctx:            ctx,
		cancel:         cancel,
		clientID:       uuid.New().String(),
	}

	return client, nil
}

// -------------------------------------------------------------------------------------------------
// 建立 WebSocket 连接
// -------------------------------------------------------------------------------------------------
func (c *WsClient) Start() error {
	c.connMu.Lock()
	defer c.connMu.Unlock()

	if c.isConnected {
		Logger(MODULE_COMMON).Warn("WebSocket client is already connected")
		return nil
	}

	var err error
	for i := 0; i < c.maxRetries; i++ {
		c.conn, _, err = c.dialer.DialContext(c.ctx, c.url, nil)
		if err == nil {
			c.isConnected = true
			go c.readLoop()
			go c.writeLoop()
			return nil
		}
		time.Sleep(c.reconnectDelay)
	}

	if err != nil {
		Logger(MODULE_COMMON).WithError(err).Error("failed to connect to WebSocket server")
		c.errorHandler(err)
	}
	return err
}

// -------------------------------------------------------------------------------------------------
// 发送消息
// -------------------------------------------------------------------------------------------------
func (c *WsClient) Send(msg Message) error {
	if !c.isConnected {
		return errors.New("not connected")
	}

	// 将 Message 转换为 JSON 字节数组
	messageBytes, err := json.Marshal(msg)
	if err != nil {
		Logger(MODULE_COMMON).WithError(err).Error("failed to marshal message")
		return err
	}

	select {
	case c.sendCh <- messageBytes:
		return nil
	case <-c.ctx.Done():
		return errors.New("client closed")
	}
}

// -------------------------------------------------------------------------------------------------
// 关闭连接
// -------------------------------------------------------------------------------------------------
func (c *WsClient) Stop() error {
	c.connMu.Lock()
	defer c.connMu.Unlock()

	if !c.isConnected {
		return nil
	}

	c.cancel()
	close(c.closeCh)
	c.isConnected = false

	if c.conn != nil {
		return c.conn.Close()
	}
	return nil
}

// -------------------------------------------------------------------------------------------------
// 读取消息循环
// -------------------------------------------------------------------------------------------------
func (c *WsClient) readLoop() {
	for {
		select {
		case <-c.ctx.Done():
			return
		default:
			_, message, err := c.conn.ReadMessage()
			if err != nil {
				c.handleError(err)
				return
			}

			if len(message) == 0 {
				continue // 跳过空消息
			}

			// 将 message 转换为 common.Message
			var msg Message
			if err := json.Unmarshal(message, &msg); err != nil {
				Logger(MODULE_COMMON).WithError(err).Error("failed to unmarshal message")
				continue // 跳过解析错误的消息
			}

			c.messageHandler(msg)
		}
	}
}

// -------------------------------------------------------------------------------------------------
// 写入消息循环
// -------------------------------------------------------------------------------------------------
func (c *WsClient) writeLoop() {
	for {
		select {
		case message := <-c.sendCh:
			err := c.conn.WriteMessage(websocket.TextMessage, message)
			if err != nil {
				c.handleError(err)
				return
			}
			Logger(MODULE_COMMON).Info("sent message: ", string(message))
		case <-c.ctx.Done():
			return
		}
	}
}

// -------------------------------------------------------------------------------------------------
// 处理错误并尝试重连
// -------------------------------------------------------------------------------------------------
func (c *WsClient) handleError(err error) {
	// 回调外部错误处理
	c.errorHandler(err)

	c.connMu.Lock()
	c.isConnected = false
	if c.conn != nil {
		c.conn.Close()
		c.conn = nil
	}
	c.connMu.Unlock()

	// 尝试重连
	go c.reconnect()
}

// -------------------------------------------------------------------------------------------------
// 自动重连
// -------------------------------------------------------------------------------------------------
func (c *WsClient) reconnect() {
	for i := 0; i < c.maxRetries; i++ {
		select {
		case <-c.ctx.Done():
			return
		default:
			if err := c.Start(); err == nil {
				return
			}
			time.Sleep(c.reconnectDelay)
		}
	}
	c.errorHandler(errors.New("max reconnect attempts reached"))
}

// -------------------------------------------------------------------------------------------------
// 返回连接状态
// -------------------------------------------------------------------------------------------------
func (c *WsClient) IsConnected() bool {
	c.connMu.Lock()
	defer c.connMu.Unlock()
	return c.isConnected
}

// -------------------------------------------------------------------------------------------------
// 返回客户端唯一标识
// -------------------------------------------------------------------------------------------------
func (c *WsClient) GetClientID() string {
	return c.clientID
}
